#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <GL/glut.h>

#include <port_comm.h>

#define VIEWPORT_WIDTH 640
#define VIEWPORT_HEIGHT 480
#define KEY_Q 113

int initGL();

GLvoid OnDisplay();
GLvoid OnReshape(GLint w, GLint h);
GLvoid OnKeyPress(unsigned char key, GLint x, GLint y);
GLvoid OnIdle();
void printUsage();

GLint g_hWindow;
PortComm *commServer;
char* dest;
int quality;
bool grey;

int main(int argc, char *argv[])
{

    //Parse arguemtns
    if (argc < 3)
    {
        printUsage();
        return 1;
    }

    int portno = atoi(argv[1]);

    commServer = new PortComm("localhost", portno, PortComm::SERVER);
    printf("Init connection complete.\n");

    char* file = argv[2];

    if(file[strlen(file)-1] != '/')
    {
        //Cat a / on to the end
        char* new_file = new char[strlen(file) + 1];
        strcpy (new_file, file);

        strcat (new_file, "/");
        file = new_file; 
    }
    const char* name = "test.jpg";
    dest = new char[strlen(file) + strlen(name) + 1];
    strcpy (dest, file);
    strcat (dest, name);
    printf("Saving to %s\n", dest);

    //Init GL
    glutInit(&argc, argv);
    if(initGL() != 0)
    {
        fprintf(stderr, "Unable to init GL Component.\n");
        return 1;
    }

    printf("Init GL complete.\n");

    quality = 95;
    grey = false;

    //Run event loop
    glutMainLoop();
    return 0;
}

int initGL()
{
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);

    g_hWindow = glutCreateWindow("2012 Rover Team");

    glClearColor(0.0, 0.0, 0.0, 0.0);

    glutDisplayFunc(OnDisplay);
    glutReshapeFunc(OnReshape);
    glutKeyboardFunc(OnKeyPress);
    glutIdleFunc(OnIdle);

    return 0;
}

GLvoid OnDisplay()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_TEXTURE_2D);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, VIEWPORT_WIDTH, VIEWPORT_HEIGHT, 0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
  
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex2f(VIEWPORT_WIDTH, 0.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex2f(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
    glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, VIEWPORT_HEIGHT);
    glEnd();

    glFlush();
    glutSwapBuffers();
}

GLvoid OnReshape(GLint w, GLint h)
{
    glViewport(0, 0, w, h);
}

GLvoid OnKeyPress(unsigned char key, int x, int y)
{
    switch(key)
    {
        case KEY_Q:
            glutDestroyWindow(g_hWindow);
            exit(0);
        case 'g':
            grey = !grey;
            break;
        case 'l':
            quality++;
            if(quality > 100)
            {
                quality = 100;
            }
            break;
        case 'k':
            quality += 10;
            if(quality > 100)
            {
                quality = 100;
            }
            break;
        case 'h':
            quality--;
            if(quality < 0)
            {
                quality = 0;
            }
            break;
        case 'j':
            quality -= 10;
            if(quality < 0)
            {
                quality = 0;
            }
            break;
    }

    glutPostRedisplay();
}

GLvoid OnIdle()
{
    cv::Mat matrix;
    unsigned char mode = quality;
    if(grey)
    {
        mode += 101;
    }
    commServer->write_port(&mode , 1);

    int size;
    unsigned char *message;
    commServer->read_port(&message, &size);
    
    printf("\r");
    if(grey)
    {
        printf("Grey ");
    }
    else
    {
        printf("Color");
    }
    printf("\t%3d\t%9d\t", quality, size);
    fflush(stdout);

    //Convert to a vector in order to convert
    cv::vector<uchar> buf;
    for(int i = 0; i < size; ++i)
    {
        buf.push_back((uchar)message[i]);
    }

    //Incoming image is in jpg, convert to Mat
    matrix = cv::imdecode(buf, 1);

    //Save image
    imwrite(dest, matrix);

    //Convert to GL RGB format
    cv::cvtColor(matrix, matrix, CV_BGR2RGB);
    gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, matrix.cols, matrix.rows, GL_RGB, GL_UNSIGNED_BYTE, matrix.data);
  
    glutPostRedisplay();
}

void printUsage()
{
    printf("usage: server <portno> <save_loc>\n");
    return;
}

